﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows.Controls;
using System.Windows.Threading;

namespace FlyingCell
{
    class GridStack : DispatcherObject
    {
        /// <summary>
        /// The list of grid
        /// </summary>
        private List<Grid> mGridList = null;

        /// <summary>
        /// Remember the viewport
        /// </summary>
        private Viewport3D mViewport = null;

        /// <summary>
        /// Remember the BoundingBox
        /// </summary>
        private BoundingBox mBoundingBox = null;

        /// <summary>
        /// GridStack status report.
        /// </summary>
        public class GridStackStatusReport : EventArgs
        {
            public GridStackStatusReport(GridStack GridStack)
            {
                GridCount = GridStack.mGridList.Count;
            }

            public int GridCount;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public GridStack()
        {
            mGridList = new List<Grid>();
        }

        /// <summary>
        /// Add a grid in the grid stack
        /// </summary>
        /// <param name="Grid">The grid to stack or null if creation is need</param>
        /// <returns>The grid stacked or null</returns>
        public Grid StartStackGrid(Grid Grid)
        {
            if (mGridList != null)
            {
                StackGridAnimationCounter = 0;

                if (Grid == null)
                {
                    Grid = new Grid(mGridList.Count.ToString());
                }

                Grid.UpdateContentCompleted += StackGrid_Completed;
                Grid.StartUpdateContent(this);
                StackGridAnimationCounter++;

                if (mGridList.Count != 0)
                {
                    mGridList[mGridList.Count - 1].Unselect();
                }
                mGridList.Add(Grid);

                // TODO: hardcoded value!!!
                Grid.mPosition = new Point3D(0, 250, 200);

                if (mViewport != null)
                {
                    Grid.AddTo3DSceneWithBounding(mViewport, mBoundingBox);
                }

                foreach (Grid GridIndex in mGridList)
                {
                    GridIndex.ZTranslationCompleted += StackGrid_Completed;
                    GridIndex.StartZTranslation(-200, 500);
                    StackGridAnimationCounter++;
                }

                return Grid;
            }

            return null;
        }

        /// <summary>
        /// Stack complete event on which external class can register a handler
        /// </summary>
        public event EventHandler<GridStackStatusReport> StackGridCompleted;

        /// <summary>
        /// Count down the completed animations
        /// </summary>
        private int StackGridAnimationCounter = 0;

        /// <summary>
        /// Translation completed event handler
        /// </summary>
        /// <param name="sender">This</param>
        /// <param name="e">EventArgs</param>
        private void StackGrid_Completed(object sender, UserControl3D.UserControl3DStatusReport e)
        {
            // TODO: Protect access to this int
            StackGridAnimationCounter--;

            if (StackGridAnimationCounter == 0)
            {
                Grid TmpGrid = mGridList[mGridList.Count - 1];
                TmpGrid.Select();
                TmpGrid.UpdateContentCompleted -= StackGrid_Completed;

                foreach (Grid GridIndex in mGridList)
                {
                    GridIndex.ZTranslationCompleted -= StackGrid_Completed;
                }

                // raise event
                if (StackGridCompleted != null)
                {
                    GridStackStatusReport Args = new GridStackStatusReport(this);
                    StackGridCompleted(this, Args);
                }
            }
        }

        /// <summary>
        /// Remove a specific grid from the stack
        /// </summary>
        /// <returns>The unstacked grid</returns>
        public Grid StartUnstackGrid()
        {
            Grid TmpGrid = null;

            if (mGridList != null)
            {
                UnstackGridAnimationCounter = 0;

                TmpGrid = mGridList[mGridList.Count - 1];

                TmpGrid.Unselect();

                foreach (Grid GridIndex in mGridList)
                {
                    GridIndex.ZTranslationCompleted += UnstackGrid_Completed;
                    GridIndex.StartZTranslation(200, 500);
                    UnstackGridAnimationCounter++;
                }
            }

            return TmpGrid;
        }

        /// <summary>
        /// Unstack grid complete event on which external class can register a handler
        /// </summary>
        public event EventHandler<GridStackStatusReport> UnstackGridCompleted;

        /// <summary>
        /// Count down the completed animations
        /// </summary>
        private int UnstackGridAnimationCounter = 0;

        /// <summary>
        /// Translation completed event handler
        /// </summary>
        /// <param name="sender">This</param>
        /// <param name="e">EventArgs</param>
        private void UnstackGrid_Completed(object sender, UserControl3D.UserControl3DStatusReport e)
        {
            // TODO: Protect access to this int
            UnstackGridAnimationCounter--;

            if (UnstackGridAnimationCounter == 0)
            {
                foreach (Grid GridIndex in mGridList)
                {
                    GridIndex.ZTranslationCompleted -= UnstackGrid_Completed;
                }

                Grid TmpGrid = mGridList[mGridList.Count - 1]; ;

                if (mViewport != null)
                {
                    TmpGrid.RemoveFrom3DScene();
                }

                mGridList.Remove(TmpGrid);

                if (mGridList.Count != 0)
                {
                    mGridList[mGridList.Count - 1].Select();
                }

                // raise event
                if (UnstackGridCompleted != null)
                {
                    GridStackStatusReport Args = new GridStackStatusReport(this);
                    UnstackGridCompleted(this, Args);
                }
            }
        }

        /// <summary>
        /// Apply a BoundingBox to the GridStack
        /// </summary>
        /// <param name="Box">The box to apply (can be null to use the registered one)</param>
        public void ApplyBoundingBox(BoundingBox Box)
        {
            // remember the bounding box
            if (Box != null)
            {
                mBoundingBox = Box;
            }

            // add everything
            foreach (Grid GridIndex in mGridList)
            {
                GridIndex.ApplyBoundingBox(mBoundingBox);
            }
        }

        /// <summary>
        /// Cancel the BoundingBox (but keep it in memory)
        /// </summary>
        public void ApplyBoundingBox()
        {
            foreach (Grid GridIndex in mGridList)
            {
                GridIndex.CancelBoundingBox();
            }
        }

        /// <summary>
        /// Add it into the 3D scene
        /// </summary>
        /// <param name="Viewport">the 3D scene (can be null to use the registered one)</param>
        /// <param name="Box">The bounding box in which GridStack is considered (can be null to use the registered one)</param>
        public void AddTo3DSceneWithBounding(Viewport3D Viewport, BoundingBox Box)
        {
            // Remember the viewport
            if (Viewport != null)
            {
                mViewport = Viewport;
            }

            // remember the bounding box
            if (Box != null)
            {
                mBoundingBox = Box;
            }

            // add everything
            foreach (Grid GridIndex in mGridList)
            {
                GridIndex.AddTo3DSceneWithBounding(mViewport, mBoundingBox);
            }
        }

        /// <summary>
        /// Add it into the 3D scene
        /// </summary>
        /// <param name="Viewport">the 3D scene (can be null to use the registered one)</param>
        public void AddTo3DScene(Viewport3D Viewport)
        {
            // Remember the viewport
            if (Viewport != null)
            {
                mViewport = Viewport;
            }

            // add everything
            foreach (Grid GridIndex in mGridList)
            {
                GridIndex.AddTo3DScene(mViewport);
            }
        }

        /// <summary>
        /// Remove it from the 3D scene
        /// </summary>
        public void RemoveFrom3DScene()
        {
            foreach (Grid GridIndex in mGridList)
            {
                GridIndex.RemoveFrom3DScene();
            }
        }

        /// <summary>
        /// Get a Grid in the stack
        /// </summary>
        /// <param name="Index">Index of the Grid in the stack (return current Grid if negative)</param>
        /// <returns>The Grid or null if not found </returns>
        public Grid GetGrid(int Index)
        {
            if (Index < 0)
            {
                if (mGridList.Count != 0)
                {
                    return mGridList[mGridList.Count - 1];
                }
                else
                {
                    return null;
                }
            }

            if (Index < mGridList.Count)
            {
                return mGridList[Index];
            }

            return null;
        }

    }
}
